﻿using System.Windows.Controls;
using System.Collections.Specialized;
using System.Windows;
using System.Collections.Generic;
using System.Collections;
using System.Collections.ObjectModel;
using System.Windows.Data;
using DeepEarth.Map.Core.Clustering;
using DeepEarth.Core.Utilities;
using System;
namespace DeepEarth.Map.Core
{
    public class LayerElementCollection : DependencyObject, IList<UIElement>, ICollection<UIElement>, IEnumerable<UIElement>, IList, ICollection, IEnumerable, INotifyCollectionChanged
    {
        // Using a DependencyProperty as the backing store for ItemVisibility.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ItemVisibilityProperty =
            DependencyProperty.RegisterAttached("ItemVisibility", typeof(Visibility), typeof(LayerElementCollection), new PropertyMetadata(ElementVisibilityChanged));

        static void ElementVisibilityChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            MapItemAdorner element = sender as MapItemAdorner;
            element.OnVisibilityChanged();
        }

        public LayerElementCollection(UIElementCollection collection)
        {
            this.allElements = collection;
        }

        private UIElementCollection allElements;
        private UIElementCollection AllElements
        {
            get
            {
                return allElements;
            }
        }

        Dictionary<UIElement, short> visibleElements = new Dictionary<UIElement, short>();

        public IEnumerable<UIElement> VisibleElements
        {
            get
            {
                return visibleElements.Keys;
            }
        }

        private Dictionary<UIElement, short> collapsedElements = new Dictionary<UIElement, short>();
        public IEnumerable<UIElement> CollapsedElements
        {
            get
            {
                return collapsedElements.Keys;
            }
        }
        
        void Show(UIElement element)
        {
            if (!visibleElements.ContainsKey(element))
            {
                collapsedElements.Remove(element);
                visibleElements.Add(element, 0);
            }
        }

        void Hide(UIElement element)
        {
            if (!collapsedElements.ContainsKey(element))
            {
                visibleElements.Remove(element);
                collapsedElements.Add(element, 0);
            }
        }

        // Returns a list of elements in the Element collection that weren't added through databinding
        public IEnumerable<UIElement> UnboundItems
        {
            get
            {
                foreach (var element in allElements)
                {
                    if (!visibleElements.ContainsKey(element) && !collapsedElements.ContainsKey(element))
                    {
                        yield return element;
                    }
                }
            }
        }

        public void Verify()
        {
            foreach (var element in AllElements)
            {
                if (!visibleElements.ContainsKey(element) && !collapsedElements.ContainsKey(element))
                {
                    visibleElements.Add(element, 0);
                }
            }
        }

        #region INotifyCollectionChanged
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        private void NotifyAdd(UIElement item, int index)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
            }
        }

        private void NotifyRemove(UIElement item, int index)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
            }
        }

        private void NotifyReplace(UIElement newItem, UIElement oldItem, int index)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newItem, oldItem, index));
            }
        }

        private void NotifyReset()
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }

        #endregion

        public UIElement this[int index]
        {
            get
            {
                return this.AllElements[index];
            }
            set
            {
                UIElement oldItem = this.AllElements[index];
                this.AllElements[index] = value;
                NotifyReplace(value, oldItem, index);
            }
        }

        public int IndexOf(UIElement item)
        {
            return this.AllElements.IndexOf(item);
        }

        public void Insert(int index, UIElement item)
        {
            this.AllElements.Insert(index, item);
            this.visibleElements.Add(item,0);
            NotifyAdd(item, index);
        }

        public void RemoveAt(int index)
        {
            UIElement item = this.AllElements[index];
            this.AllElements.RemoveAt(index);
            NotifyRemove(item, index);
        }

        public void Add(UIElement item)
        {
            this.AllElements.Add(item);
            this.visibleElements.Add(item, 0);

            NotifyAdd(item, this.AllElements.Count - 1);

            AttachAdornerVisibilityListener(item);
        }

        void AttachAdornerVisibilityListener(UIElement item)
        {
            MapItemAdorner adorner = item as MapItemAdorner;

            if (adorner == null)
            {
                return;
            }

            Binding visBinding = new Binding("Visibility") { Source = item, Mode = BindingMode.OneWay };
            BindingOperations.SetBinding(item, LayerElementCollection.ItemVisibilityProperty, visBinding);

            WeakEventListener<LayerElementCollection, MapItemAdorner> weakListener = new WeakEventListener<LayerElementCollection, MapItemAdorner>(this, adorner);
            adorner.VisibilityChanged += weakListener.OnEvent;

            weakListener.DetatchAction = (listener, source) =>
            {
                source.VisibilityChanged -= listener.OnEvent;
            };

            weakListener.EventAction = (listener, source, eventArgs) =>
            {
                MapItemAdornerEventArgs ea = eventArgs as MapItemAdornerEventArgs;

                if (ea.IsVisible)
                {
                    listener.Show(source);
                }
                else
                {
                    listener.Hide(source);
                }
          
            };
        }

        public void Clear()
        {
            this.AllElements.Clear();
            this.visibleElements.Clear();
            this.collapsedElements.Clear();
            NotifyReset();
        }

        public bool Contains(UIElement item)
        {
            return this.AllElements.Contains(item);
        }

        public void CopyTo(UIElement[] array, int arrayIndex)
        {
            this.AllElements.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return this.AllElements.Count; }
        }

        public bool Remove(UIElement item)
        {
            int index = this.AllElements.IndexOf(item);
            bool removed = this.AllElements.Remove(item);
            this.visibleElements.Remove(item);
            this.collapsedElements.Remove(item);

            if (removed)
            {
                NotifyRemove(item, index);
            }

            return removed;
        }

        public IEnumerator<UIElement> GetEnumerator()
        {
            return this.AllElements.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.AllElements.GetEnumerator();
        }

        bool ICollection<UIElement>.IsReadOnly
        {
            get { return false; }
        }

        bool IList.IsFixedSize
        {
            get { return false; }
        }

        int IList.Add(object o)
        {
            this.Add((UIElement)o);
            return this.AllElements.Count - 1;
        }

        bool IList.Contains(object value)
        {
            return this.Contains((UIElement)value);
        }

        int IList.IndexOf(object value)
        {
            return this.AllElements.IndexOf((UIElement)value);
        }

        void IList.Insert(int index, object value)
        {
            this.Insert(index, (UIElement)value);           
        }

        bool IList.IsReadOnly
        {
            get { return false; }
        }

        object IList.this[int index]
        {
            get
            {
                return this[index];
            }
            set
            {
                UIElement element = value as UIElement;

                if (element != null)
                {
                    this[index] = element;
                }
            }
        }


        void IList.Remove(object value)
        {
            this.Remove((UIElement)value);
        }

        void ICollection.CopyTo(System.Array array, int index)
        {
            throw new System.NotImplementedException();
        }

        bool ICollection.IsSynchronized
        {
            get { throw new System.NotImplementedException(); }
        }

        object ICollection.SyncRoot
        {
            get { throw new System.NotImplementedException(); }
        }
    }
}
